/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.spi.entity;

import com.inspur.edp.bef.api.lcp.IStandardLcp;
import com.inspur.edp.bef.api.lcp.LcpFactoryManagerUtils;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.entity.entity.FieldType;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.*;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefEntityResInfoImpl;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefModelResInfoImpl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class AssociationInfo {

  private String config;
  private String nodeCode;
  private String mainCode;
  private HashMap<String, Class> refPropInfoTypes;
  private AssociationEnableState enableState = AssociationEnableState.Enabled;
  private String privateSourceColumn;
  private String privateTargetColumn;
  private TreeMap<String, DataTypePropertyInfo> refPropInfos;
  private TreeMap<String, DataTypePropertyInfo> enrichedRefPropInfos;
  private boolean keepAssoPropertyForExpression = false;
  private boolean isRefInit = false;
  private String where;
  private ArrayList<AssoCondition> assoConditions;

  public AssociationInfo() {

  }

  public AssociationInfo(String config, String nodeCode, String privateSourceColumn, String privateTargetColumn) {
    this(config, nodeCode, nodeCode, AssociationEnableState.Enabled, privateSourceColumn, privateTargetColumn);
  }

  public AssociationInfo(String config, String nodeCode, String mainCode, AssociationEnableState enableState, String privateSourceColumn, String privateTargetColumn) {
    this.config = config;
    this.nodeCode = nodeCode;
    this.mainCode = mainCode;
    this.enableState = enableState;
    this.privateSourceColumn = privateSourceColumn;
    this.privateTargetColumn = privateTargetColumn;
  }

  public ArrayList<AssoCondition> getAssoConditions() {
    return assoConditions;
  }

  public void setAssoConditions(ArrayList<AssoCondition> assoConditions) {
    this.assoConditions = assoConditions;
  }


  public String getWhere() {
    return where;
  }

  public void setWhere(String where) {
    this.where = where;
  }


  public final String getConfig() {
    return config;
  }

  public final void setConfig(String value) {
    config = value;
  }

  public final String getNodeCode() {
    return nodeCode;
  }

  public final void setNodeCode(String value) {
    nodeCode = value;
  }

  public final String getMainCode() {
    return mainCode;
  }

  public final void setMainCode(String value) {
    mainCode = value;
  }

  public AssociationEnableState getEnableState() {
    return enableState;
  }

  public void setEnableState(AssociationEnableState value) {
    enableState = value;
  }

  public String getPrivateSourceColumn() {
    return privateSourceColumn;
  }

  public void setPrivateSourceColumn(String privateSourceColumn) {
    this.privateSourceColumn = privateSourceColumn;
  }

  public String getPrivateTargetColumn() {
    return privateTargetColumn;
  }

  public void setPrivateTargetColumn(String privateTargetColumn) {
    this.privateTargetColumn = privateTargetColumn;
  }

  /**
   * 关联带出字段(Key:属性名，Value:typeof(propInfo))
   */
  public java.util.HashMap<String, java.lang.Class> getRefPropInfoTypes() {
    if (refPropInfoTypes == null) {
      refPropInfoTypes = new java.util.HashMap<String, java.lang.Class>();
    }
    return refPropInfoTypes;
  }

  public TreeMap<String, DataTypePropertyInfo> getRefPropInfos() {
    tryInitRefProperties();
    return innerGetRefPropInfos();
  }

  private void tryInitRefProperties() {
    if (isRefInit == true)
      return;
    boolean hasPropNotInit = false;
    for (int i = 0; i < 5; i++) {
      hasPropNotInit = false;
      for (Map.Entry<String, DataTypePropertyInfo> entry : innerGetRefPropInfos().entrySet()) {
        if (entry.getValue() instanceof RefDataTypePropertyInfo == false)
          continue;
        RefDataTypePropertyInfo refDataTypePropertyInfo = (RefDataTypePropertyInfo) entry.getValue();
        if (refDataTypePropertyInfo == null || refDataTypePropertyInfo.isRefInit())
          continue;
        DataTypePropertyInfo refedDataTypePropertyInfo = getRefedDataTypePropertyInfo(refDataTypePropertyInfo.getRefPropertyName());
        if (refedDataTypePropertyInfo instanceof RefDataTypePropertyInfo) {
          RefDataTypePropertyInfo refDataTypePropertyInfo1 = (RefDataTypePropertyInfo) refedDataTypePropertyInfo;
          if (refDataTypePropertyInfo1.isRefInit() == false) {
            hasPropNotInit = true;
            continue;
          }
        }
          DataTypePropertyInfo.setPropertyInfoByRefed(refDataTypePropertyInfo, refedDataTypePropertyInfo);
          refDataTypePropertyInfo.setRefInit(true);

      }
      for(Map.Entry<String,DataTypePropertyInfo> entry:innerGetEnrichedRefPropInfos().entrySet())
      {
        if (entry.getValue() instanceof RefDataTypePropertyInfo == false)
          continue;
        RefDataTypePropertyInfo refDataTypePropertyInfo = (RefDataTypePropertyInfo) entry.getValue();
        if (refDataTypePropertyInfo == null || refDataTypePropertyInfo.isRefInit())
          continue;
        DataTypePropertyInfo refedDataTypePropertyInfo = getRefedDataTypePropertyInfo(refDataTypePropertyInfo.getRefPropertyName());
        if (refedDataTypePropertyInfo instanceof RefDataTypePropertyInfo) {
          RefDataTypePropertyInfo refDataTypePropertyInfo1 = (RefDataTypePropertyInfo) refedDataTypePropertyInfo;
          if (refDataTypePropertyInfo1.isRefInit() == false) {
            hasPropNotInit = true;
            continue;
          }
        }
          DataTypePropertyInfo.setPropertyInfoByRefed(refDataTypePropertyInfo, refedDataTypePropertyInfo);
          refDataTypePropertyInfo.setRefInit(true);
      }
      if (hasPropNotInit == false)
        break;
    }
    if (hasPropNotInit)
      throw new RuntimeException("存在关联带出属性未初始化完成。");


    isRefInit = true;
  }

  private TreeMap<String, DataTypePropertyInfo> innerGetRefPropInfos()
  {
    if (refPropInfos == null) {
      refPropInfos = new TreeMap<String, DataTypePropertyInfo>(String.CASE_INSENSITIVE_ORDER);
    }

    return refPropInfos;
  }

  public void addRefProp(String key, DataTypePropertyInfo refInfo) {
    innerGetRefPropInfos().put(key, refInfo);
  }

  public TreeMap<String, DataTypePropertyInfo> getEnrichedRefPropInfos() {
    tryInitRefProperties();
    return innerGetEnrichedRefPropInfos();
  }

  private TreeMap<String, DataTypePropertyInfo> innerGetEnrichedRefPropInfos() {
    if (enrichedRefPropInfos == null) {
      enrichedRefPropInfos = new TreeMap<String, DataTypePropertyInfo>(String.CASE_INSENSITIVE_ORDER);
    }

    return enrichedRefPropInfos;
  }

  public void addEnrichedRefProp(String key, DataTypePropertyInfo refInfo) {
    innerGetEnrichedRefPropInfos().put(key, refInfo);
  }

  public final void addRefProperty(String propertyName,String refPropertyName) {
    addRefProperty(propertyName, "", refPropertyName);
  }

//  public final  void addRefProperty(String propertyName,String refPropertyName)

  public final void addRefProperty(String propertyName,String propertyDisplayKey,String refPropertyName)
  {
    addRefProp(propertyName, initRefProeprty(propertyName,propertyDisplayKey,refPropertyName));
  }

  private RefDataTypePropertyInfo initRefProeprty(String propertyName,String propertyDisplayKey,String refPropertyName)
  {
    RefDataTypePropertyInfo refDataTypePropertyInfo= new RefDataTypePropertyInfo(propertyName,propertyDisplayKey,false,false,0,0, FieldType.String, ObjectType.Normal,null,null,refPropertyName);
    refDataTypePropertyInfo.setRefInit(false);
    return refDataTypePropertyInfo;
  }

  protected DataTypePropertyInfo interalGetRefProperty(String propertyName, String propertyDisplayKey,
      String refPropertyName) {
    DataTypePropertyInfo dataTypePropertyInfo = getRefedDataTypePropertyInfo(refPropertyName);

    RefDataTypePropertyInfo result=  new  RefDataTypePropertyInfo(propertyName,
        propertyDisplayKey, dataTypePropertyInfo.getRequired(),
        dataTypePropertyInfo.getEnableRtrim(),
        dataTypePropertyInfo.getLength(), dataTypePropertyInfo.getPrecision(),
        dataTypePropertyInfo.getFieldType(),
        dataTypePropertyInfo.getObjectType(),
        (BasePropertyInfo) dataTypePropertyInfo.getObjectInfo(),
        dataTypePropertyInfo.getDefaultValue(), dataTypePropertyInfo.isMultiLaguange(),
        dataTypePropertyInfo.getIsBigNumber(),dataTypePropertyInfo.getPropertyName());
    result.setRefInit(true);
    return result;
  }

  private DataTypePropertyInfo getRefedDataTypePropertyInfo(String refPropertyName) {
    CefModelResInfoImpl modelResInfo = (CefModelResInfoImpl)((IStandardLcp) LcpFactoryManagerUtils
        .getBefLcpFactory().createCMManager(config)).getModelInfo();
    CefEntityResInfoImpl entityResInfo =null;
    try {
      entityResInfo=   (CefEntityResInfoImpl) modelResInfo
              .getCustomResource(getNodeCode());
    }
    catch (Exception e)
    {
      entityResInfo=   (CefEntityResInfoImpl) modelResInfo
              .getCustomResource(getMainCode());
    }
    DataTypePropertyInfo dataTypePropertyInfo = entityResInfo.getEntityTypeInfo()
        .getPropertyInfoWithoutCache(refPropertyName);
    if(dataTypePropertyInfo==null)
        dataTypePropertyInfo=getRefPropertyInfo(entityResInfo,refPropertyName);

    if(dataTypePropertyInfo==null)
      throw new CefException("找不到关联带出属性："+refPropertyName);
    return dataTypePropertyInfo;
  }

  private DataTypePropertyInfo getRefPropertyInfo(CefEntityResInfoImpl entityResInfo,String refPropertyName) {
    for (Map.Entry<String, DataTypePropertyInfo> propertyInfoEntry : entityResInfo.getEntityTypeInfo().getPropertyInfos().entrySet()) {
        if(refPropertyName.equals( propertyInfoEntry.getValue().getPropertyName()))
            return propertyInfoEntry.getValue();
      if (propertyInfoEntry.getValue().getObjectInfo() instanceof AssocationPropertyInfo) {
        AssociationInfo associationInfo = ((AssocationPropertyInfo) propertyInfoEntry.getValue().getObjectInfo()).getAssociationInfo();
        for (Map.Entry<String, DataTypePropertyInfo> refPropertyEntry : associationInfo.getRefPropInfos().entrySet()) {
          if (refPropertyEntry.getValue().getPropertyName().equals(refPropertyName))
            return refPropertyEntry.getValue();
        }

        if (associationInfo.getEnrichedRefPropInfos() != null && associationInfo.getEnrichedRefPropInfos().size() != 0) {
          for (Map.Entry<String, DataTypePropertyInfo> refPropertyEntry : associationInfo.getEnrichedRefPropInfos().entrySet()) {
            if (refPropertyEntry.getValue().getPropertyName().equals(refPropertyName))
              return refPropertyEntry.getValue();
          }
        }
      }
      if(propertyInfoEntry.getValue().getObjectInfo() instanceof  SimpleAssoUdtPropertyInfo)
      {
        AssociationInfo associationInfo=((SimpleAssoUdtPropertyInfo)propertyInfoEntry.getValue().getObjectInfo()).getAssoInfo().getAssociationInfo();
        for (Map.Entry<String, DataTypePropertyInfo> refPropertyEntry : associationInfo.getRefPropInfos().entrySet()) {
          if (refPropertyEntry.getValue().getPropertyName().equals(refPropertyName))
            return refPropertyEntry.getValue();
        }

        if (associationInfo.getEnrichedRefPropInfos() != null && associationInfo.getEnrichedRefPropInfos().size() != 0) {
          for (Map.Entry<String, DataTypePropertyInfo> refPropertyEntry : associationInfo.getEnrichedRefPropInfos().entrySet()) {
            if (refPropertyEntry.getValue().getPropertyName().equals(refPropertyName))
              return refPropertyEntry.getValue();
          }
        }
      }
    }
    return null;
  }

  public final void addEnrichedRefProperty(String propertyName,String refPropertyName)
  {
    addEnrichedRefProperty(propertyName,"",refPropertyName);
  }
  public final void addEnrichedRefProperty(String propertyName,String propertyDisplayKey,String refPropertyName)
  {
    addEnrichedRefProp(propertyName,initRefProeprty(propertyName,propertyDisplayKey,refPropertyName));
  }

  public final boolean isKeepAssoPropertyForExpression() {
    return keepAssoPropertyForExpression;
  }

  public final void setKeepAssoPropertyForExpression(boolean keepAssoPropertyForExpression) {
    this.keepAssoPropertyForExpression = keepAssoPropertyForExpression;
  }
}
